const SPECIAL_CHARS_REGEXP: RegExp = /([:\-_]+(.))/g;
const MOZ_HACK_REGEXP: RegExp = /^moz([A-Z])/;

/**
 * 创建一个DOM元素
 * @param tagName
 * @param className
 * @param container
 * @param id
 */
export const create = (tagName: string, className: string, container: any, id: string): HTMLElement => {
    let el: HTMLElement = document.createElement(tagName);
    el.className = className || '';
    if (id) {
        el.id = id;
    }
    if (container) {
        container.appendChild(el);
    }
    return el;
};

/**
 * 根据ID获取DOM要素
 * @param id
 */
export const getElement = (id: any): any => {
    return typeof id === 'string' ? document.getElementById(id) : id;
};

/**
 * 移除要DOM元素
 * @param el
 */
export const remove = (el: HTMLElement): void => {
    let parent: any = el.parentNode;
    if (parent) {
        parent.removeChild(el);
    }
};

/**
 * 清空DOM元素下的子元素
 * @param el
 */
export const empty = (el: any): void => {
    while (el.firstChild) {
        el.removeChild(el.firstChild);
    }
};

/**
 * 创建一个隐藏的DOM元素
 * @param tagName
 * @param parent
 * @param id
 */
export const createHidden = (tagName: string, parent: any, id: string): HTMLElement => {
    let element: HTMLElement = document.createElement(tagName);
    element.style.display = 'none';
    if (id) {
        element.id = id;
    }
    if (parent) {
        parent.appendChild(element);
    }
    return element;
};

/**
 * 去除字符串空格
 * @param string
 */
const trim = (string: string): string => {
    return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '');
};

/**
 * 驼峰命名法
 * @param name
 */
const camelCase = (name: string): string => {
    return name.replace(SPECIAL_CHARS_REGEXP, function (_, separator, letter, offset) {
        return offset ? letter.toUpperCase() : letter;
    }).replace(MOZ_HACK_REGEXP, 'Moz$1');
};

/**
 * 添加事件侦听
 */
export const on = ((): Function | undefined => {
    if (document.addEventListener !== undefined) {
        return (element: any, event: any, handler: any): void => {
            if (element && event && handler) {
                element.addEventListener(event, handler, false);
            }
        };
    }
})();

/**
 * 移除事件侦听
 */
export const off = ((): Function | undefined => {
    if (document.removeEventListener !== undefined) {
        return (element: HTMLElement, event: any, handler: any): void => {
            if (element && event) {
                element.removeEventListener(event, handler, false);
            }
        };
    }
})();

/**
 * 只监听一次
 * @param el
 * @param event
 * @param fn
 */
export const once = function (el: HTMLElement, event: any, fn: any) {
    const listener: Function = () => {
        if (fn) {
            fn(arguments);
        }
        off && off(el, event, listener);
    };
    on && on(el, event, listener);
};

/**
 * 是否有类名
 * @param el
 * @param cls
 */
export const hasClass = (el: HTMLElement, cls: string): boolean => {
    if (!el || !cls) return false;
    if (cls.indexOf(' ') !== -1) throw new Error('className should not contain space.');
    if (el.classList) {
        return el.classList.contains(cls);
    } else {
        return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1;
    }
};

/**
 * 添加类名
 * @param el
 * @param cls
 */
export const addClass = (el: HTMLElement, cls: string): void => {
    if (!el) return;
    let curClass: string = el.className;
    let classes: Array<string> = (cls || '').split(' ');
    for (let i: number = 0, j: number = classes.length; i < j; i++) {
        let clsName: string = classes[i];
        if (!clsName) continue;
        if (el.classList) {
            el.classList.add(clsName);
        } else if (!hasClass(el, clsName)) {
            curClass += ' ' + clsName;
        }
    }
    if (!el.classList) {
        el.className = curClass;
    }
};

/**
 * 移除类名
 * @param el
 * @param cls
 */
export const removeClass = (el: HTMLElement, cls: string): void => {
    if (!el || !cls) return;
    const classes: Array<string> = cls.split(' ');
    let curClass: string = ` ${el.className} `;
    for (let i: number = 0, j: number = classes.length; i < j; i++) {
        let clsName: string = classes[i];
        if (!clsName) continue;
        if (el.classList) {
            el.classList.remove(clsName);
        } else if (hasClass(el, clsName)) {
            curClass = curClass.replace(' ' + clsName + ' ', ' ');
        }
    }
    if (!el.classList) {
        el.className = trim(curClass);
    }
};

/**
 * 获取DOM元素的样式
 * @param element
 * @param styleName
 */
export const getStyle = (element: HTMLElement, styleName: any): any => {
    if (!element || !styleName) return null;
    styleName = camelCase(styleName);
    if (styleName === 'float') {
        styleName = 'cssFloat';
    }
    try {
        const computed = document.defaultView?.getComputedStyle(element, '');
        return (element.style[styleName]) || (computed !== undefined ? computed[styleName] : null);
    } catch (e) {
        return element.style[styleName];
    }
};

/* istanbul ignore next */
export const setStyle = (element: HTMLElement, styleName: any, value: any): void => {
    if (!element || !styleName) return;
    if (typeof styleName === 'object') {
        for (const prop in styleName) {
            if (styleName.hasOwnProperty(prop)) {
                setStyle(element, prop, styleName[prop]);
            }
        }
    } else {
        styleName = camelCase(styleName);
        if (styleName === 'opacity') {
            element.style.filter = isNaN(value) ? '' : 'alpha(opacity=' + value * 100 + ')';
        } else {
            element.style[styleName] = value;
        }
    }
};